/*
 *    Description:  lte ip connection
 *          Author:  dimmalex (dim), dimmalex@gmail.com
 *      Company:  HP
 */

#include "land/farm.h"
#include "landnet/landnet.h"
#include <ifaddrs.h>



/* set the dial failed to restart system */
static int count_failed( const char *obj, const char *dev, talk_t failed )
{
    int recon2reboot;
    int recon2reset;
    int reconfailed;
    const char *ptr;

	reconfailed = count_get( obj, "reconfailed" );

    /* get the reboot max configure */
    recon2reboot = 0;
    ptr = json_get_string( failed, "recon2reboot" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        recon2reboot = atoi( ptr );
    }
    if ( recon2reboot > 0 )
    {
        if ( reconfailed >= recon2reboot )
        {
            warn( "%s need to restart because reconnect maxtime(%d)", obj, reconfailed );
            count_set( obj, "reconfailed", 0 );
            com_scall( MACHINE_COM, "restart", NULL );
            return 1;
        }
    }

    /* get the reset max configure */
    recon2reset = 0;
    ptr = json_get_string( failed, "recon2reset" );
    if ( ptr == NULL || *ptr == '\0' )
    {
    	ptr = "10";
    }
	recon2reset = atoi( ptr );
    if ( recon2reset > 0 )
    {
        if ( reconfailed >= recon2reset )
        {
            warn( "%s need to reset the modem because reconnect maxtime(%d)", obj, reconfailed );
            count_set( obj, "reconfailed", 0 );
            com_srun( dev, "reset", "pure" );
            return 1;
        }
    }

	if ( reconfailed > 0 )
	{
		ndebug( "%s reconnect %d time", obj, reconfailed );
	}
    return 0;
}
static boole mode_static( const char *object, const char *ifdev, const char *device, talk_t cfg, talk_t info )
{
    talk_t v;
    const char *ptr;

    /* tell the network frame online */
    json_set_string( info, "mode", "static" );
    json_set_string( info, "ifname", object );
    json_set_string( info, "ifdev", ifdev );
    json_set_string( info, "device", device );
	v = json_get_value( cfg, "static" );
    ptr = json_get_string( v, "gw" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "gw", ptr );
    }
    ptr = json_get_string( v, "dns" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "dns", ptr );
    }
    ptr = json_get_string( v, "dns2" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "dns2", ptr );
    }
    ptr = json_get_string( v, "domain" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "domain", ptr );
    }

    return true;
}
static boole method_manual( const char *object, const char *ifdev, const char *device, talk_t cfg, talk_t info )
{
    talk_t v;
    const char *ptr;

    /* tell the network frame online */
    json_set_string( info, "method", "manual" );
    json_set_string( info, "ifname", object );
    json_set_string( info, "ifdev", ifdev );
    json_set_string( info, "device", device );
	v = json_get_value( cfg, "manual" );
    ptr = json_get_string( v, "hop" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "hop", ptr );
    }
    ptr = json_get_string( v, "resolve" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "resolve", ptr );
    }
    ptr = json_get_string( v, "resolve2" );
    if ( NULL != ptr && *ptr != '\0' )
    {
        json_set_string( info, "resolve2", ptr );
    }

    return true;
}
/* meke the ppp options file, succeed return 0 */
static void ppp_config_build( FILE *opfile, FILE *scfile, talk_t pppcfg, talk_t oper )
{
    const char *ptr;
    const char *name;
    const char *passwd;
    const char *auth;
    const char *mtu;
    const char *ppp_pppopt;
    const char *oper_pppopt;

    if ( opfile == NULL || scfile == NULL )
    {
        return;
    }
    name = json_get_string( oper, "user" );
    passwd = json_get_string( oper, "passwd" );
    auth = json_get_string( oper, "auth" );
    oper_pppopt = json_get_string( oper, "opt" );
    mtu = json_get_string( pppcfg, "mtu" );
    ppp_pppopt = json_get_string( pppcfg, "pppopt" );

    /* username */
    if ( name != NULL && *name != '\0' )
    {
        if ( passwd != NULL && *passwd != '\0' )
        {
            fprintf( scfile, "\"%s\"\t*\t\"%s\"\t*\n", name, passwd );
        }
        else
        {
            fprintf( scfile, "\"%s\"\t*\t\"\"\t*\n", name );
        }
        fprintf( opfile, "name \"%s\"\n", name );
        if ( auth != NULL && *auth != '\0' )
        {
            if ( 0 == strcmp( auth, "pap" ) )
            {
                fprintf( opfile, "require-pap\n");
            }
            else if ( 0 == strcmp( auth, "chap" ) )
            {
                fprintf( opfile, "require-chap\n");
            }
        }
    }

    /* mtu */
    if ( mtu != NULL && *mtu != '\0' )
    {
        fprintf( opfile, "mtu %s\n", mtu );
    }

    /* lcp echo  */
    ptr = json_get_string( pppcfg, "lcp_echo_interval" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        fprintf( opfile, "lcp-echo-interval %s\n", ptr );
    }
    ptr = json_get_string( pppcfg, "lcp_echo_failure" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        fprintf( opfile, "lcp-echo-failure %s\n", ptr );
    }
    
    /* network pppopt  */
    if ( oper_pppopt != NULL && *oper_pppopt != '\0' )
    {
        char *tok;
        char *tokkey;
        char buffer[TEXTLINE_SIZE];
        
        memset( buffer, 0, sizeof(buffer) );
        strncpy( buffer, oper_pppopt, sizeof(buffer)-1 );
        tokkey = tok = buffer;
        while( tokkey != NULL && *tok != '\0' )
        {
            tokkey = strstr( tok, ";" );
            if ( tokkey != NULL )
            {
                *tokkey = '\0';
            }
            fprintf( opfile, "%s\n", tok );
            tok = tokkey+1;
        }
    }
    /* pppopt  */
    if ( ppp_pppopt != NULL && *ppp_pppopt != '\0' )
    {
        char *tok;
        char *tokkey;
        char buffer[TEXTLINE_SIZE];
        
        memset( buffer, 0, sizeof(buffer) );
        strncpy( buffer, ppp_pppopt, sizeof(buffer)-1 );
        tokkey = tok = buffer;
        while( tokkey != NULL && *tok != '\0' )
        {
            tokkey = strstr( tok, ";" );
            if ( tokkey != NULL )
            {
                *tokkey = '\0';
            }
            fprintf( opfile, "%s\n", tok );
            tok = tokkey+1;
        }
    }
    return;
}/* make the ppp chat */
static void ppp_chat_build( const char *chatfile, talk_t pppcfg, talk_t oper )
{
#define LTE_APNCHAT_FORMAT \
"ABORT 'ERROR'\n"\
"ABORT 'BUSY'\n"\
"ABORT 'NO CARRIER'\n"\
"'' AT\n"\
"TIMEOUT 5\n"\
"OK AT+CGDCONT=%s,\"%s\",\"%s\"\n"\
"TIMEOUT 15\n"\
"OK ATD%s\n"\
"TIMEOUT 25\n"\
"CONNECT ''"
#define LTE_NOAPNCHAT_FORMAT \
"ABORT 'ERROR'\n"\
"ABORT 'BUSY'\n"\
"ABORT 'NO CARRIER'\n"\
"'' AT\n"\
"TIMEOUT 5\n"\
"OK ATD%s\n"\
"TIMEOUT 25\n"\
"CONNECT ''"
    const char *apn;
    const char *dial;
    const char *type;
    const char *cid;
    const char *iptype;

    apn = json_get_string( oper, "apn" );
    dial = json_get_string( oper, "dial" );
    if ( apn != NULL && *apn != '\0' )
    {
        type = json_get_string( oper, "type" );
        iptype = "IP";
        if ( type != NULL && 0 == strcasecmp( type, "ipv4v6" ) )
        {
            iptype = "IPV4V6";
        }
        else if ( type != NULL && 0 == strcasecmp( type, "ipv4" ) )
        {
            iptype = "IPV4";
        }
        else if ( type != NULL && 0 == strcasecmp( type, "ipv6" ) )
        {
            iptype = "IPV6";
        }
        cid = json_get_string( oper, "cid" );
        if ( cid == NULL || *cid == '\0' )
        {
            cid = "1";
        }
        write_string( chatfile, LTE_APNCHAT_FORMAT, cid, iptype, apn, dial?:"" );
    }
    else
    {
        write_string( chatfile, LTE_NOAPNCHAT_FORMAT, dial?:"" );
    }
    return;
}
/* online the network, return -1 on error, 1 on failed, 0 on succeed  */
static int ppp_client_connect( const char *object, const char *ifdev, talk_t cfg, talk_t status )
{
    talk_t pppcfg;
    talk_t operator;
    const char *ptr;
    char buf[PATHNAME_SIZE];
    char imsicut[IDENTIFY_SIZE];
    FILE *pppoptions_fd;
    FILE *pppsecrets_fd;
    char pppoptions[PATHNAME_SIZE];
    char pppsecrets[PATHNAME_SIZE];
    char pppchat[PATHNAME_SIZE];

    /* get the network operator */
    operator = NULL;
    ptr = json_get_string( cfg, "operator" );
    if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
    {
        operator = json_get_value( cfg, "operator_cfg" );
    }
    if ( operator == NULL )
    {
        ptr = json_get_string( status, "mnc" );
        if ( ptr != NULL && *ptr != '\0' )
        {
            snprintf( buf, sizeof(buf), MODEM_PROJECT"%c%s", OBJECT_GAP_ID, "operator" );
            operator = config_sget( buf, ptr );
        }
        if ( operator == NULL )
        {
            ptr = json_get_string( status, "imsi" );
            if ( ptr != NULL )
            {
                strncpy( imsicut, ptr, 5 );
                imsicut[5] = '\0';
                snprintf( buf, sizeof(buf), MODEM_PROJECT"%c%s", OBJECT_GAP_ID, "operator" );
                operator = config_sget( buf, imsicut );
            }
        }
    }
    if ( operator == NULL )
    {
        warn( "%s operator infomation not found, use the default", object );
		operator = string2talk( "{\"name\":\"unspecified\",\"dial\":\"*99#\",\"apn\":\"internet\"}" );
		sleep( 3 );
    }

    /* init the tmp value */
    pppoptions_fd = NULL;
    pppsecrets_fd = NULL;
    /* get the ppp var file */
    project_var_path( pppoptions, sizeof(pppoptions), PROJECT_ID, "%s.pppopt", object );
    project_var_path( pppsecrets, sizeof(pppsecrets), PROJECT_ID, "%s.pppsec", object );
    project_var_path( pppchat, sizeof(pppchat), PROJECT_ID, "%s.pppchat", object );
    if ( NULL == ( pppoptions_fd = fopen( pppoptions, "w+" ) ) )
    {
        faulting( "open the file(%s) error", pppoptions ); 
        sleep( 10 );
        goto exit;
    }
    if ( NULL == ( pppsecrets_fd = fopen( pppsecrets, "w+" ) ) )
    {
        faulting( "open the file(%s) error", pppsecrets );
        sleep( 10 );
        goto exit;
    }
    /* make the pre config */
    fprintf( pppoptions_fd, "noauth\n" );
    fprintf( pppoptions_fd, "nomppe\n" );
    fprintf( pppoptions_fd, "noipdefault\n" );
    fprintf( pppoptions_fd, "usepeerdns\n" );
    fprintf( pppoptions_fd, "nodetach\n" );
    fprintf( pppoptions_fd, "pap-timeout 60\n" );
    fprintf( pppoptions_fd, "lock\n" );
    fprintf( pppoptions_fd, "modem\n" );
    fprintf( pppoptions_fd, "noendpoint\n" );
    fprintf( pppoptions_fd, "nomagic\n" );
    fprintf( pppoptions_fd, "nolog\n" );
    //fprintf( pppoptions_fd, "nopersist\n" );
    fprintf( pppoptions_fd, "holdoff 10\n" );

    /* make the basic config */
    pppcfg = json_get_value( cfg, "ppp" );
    ppp_config_build( pppoptions_fd, pppsecrets_fd, pppcfg, operator );

    /* make the basic path */
    fprintf( pppoptions_fd, "pap-file %s\n", pppsecrets );
    fprintf( pppoptions_fd, "chap-file %s\n", pppsecrets );
    fprintf( pppoptions_fd, "srp-file %s\n", pppsecrets );
    fprintf( pppoptions_fd, "online-id %s\n", object );
    /* make the chat script */;
    ppp_chat_build( pppchat, pppcfg, operator );
    fprintf( pppoptions_fd, "connect \"chat -v -f %s\"\n", pppchat );
    fclose( pppoptions_fd );
    fclose( pppsecrets_fd );
    pppoptions_fd = NULL;
    pppsecrets_fd = NULL;

    /* ppp dial */
    ptr = json_get_string( status, "mtty" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        execlp( "pppd", "pppd", ptr, "file", pppoptions, (char*)0 );
        faulting( "exec the pppd error" );    
    }
    else
    {
        fault( "cannot find the modem tty to ppp dial for %s", object );
    }

exit:
    if ( pppoptions_fd != NULL )
    {
        fclose( pppoptions_fd );
    }
    if ( pppsecrets_fd != NULL )
    {
        fclose( pppsecrets_fd );
    }
    if ( operator != json_get_value( cfg, "operator_cfg" ) )
    {
        talk_free( operator );
    }
    return 1;
}



const char *_intro( obj_t this, param_t param )
{
    const char *obj;
    const char *str = \
"{"\
"\n    \"setup\":\"setup the ifname\","\
"\n    \"shut\":\"shutdown the ifname\","\
"\n    \"ifdev\":\"get the ifdev\","\
"\n    \"device\":\"get the device\","\
"\n    \"status\":\"get the status\""\
"\n}\n";
    obj = obj_name( this );
    if ( obj == NULL )
    {
        return NULL;
    }
    else if ( 0 == strcmp( obj, COM_ID ) )
    {
        return NULL;
    }
    return str;
}
talk_t _setup( obj_t this, param_t param )
{
    talk_t v;
    talk_t cfg;
    talk_t ret;
    const char *ptr;
    const char *obj;
    const char *object;
    char buf[IDENTIFY_SIZE];
    char ifdev[IDENTIFY_SIZE];
    char path[PATHNAME_SIZE];

    /* get the com name */
    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    /* get the ifname configure */
    cfg = config_get( this, NULL ); 
    /* exit when the status is disable */
    ptr = json_get_string( cfg, "status" );
    if ( ptr != NULL && 0 == strcmp( ptr, "disable" ) )
    {
        talk_free( cfg );
        return tfalse;
    }
    /* get the ifdev */
    if ( ifdev_get( object, ifdev, sizeof(ifdev) ) == NULL )
    {
        talk_free( cfg );
        return tfalse;
    }
	if ( com_sexist( ifdev, NULL ) == false )
	{
        talk_free( cfg );
        return tfalse;
	}
    /* setup record file, avoid duplicate setup */
    project_var_path( path, sizeof(path), PROJECT_ID, "%s.st", obj );
    v = json_load( path );
    if ( v != NULL )
    {
        ndebug( "%s already startup", object );
        talk_free( v );
        talk_free( cfg );
        return ttrue;
    }
    v = json_create();
    snprintf( buf, sizeof(buf), "%lu", time(NULL) );
    json_set_string( v, "starttime", buf );
    json_save( v , path );
    talk_free( v );

    /* run the app connection */
    ret = tfalse;
    info( "%s startup", object );
    if ( serv_start( this, "service", NULL, object ) == true )
    {
        ret = ttrue;
    }

    talk_free( cfg );
    return ret;
}
talk_t _shut( obj_t this, param_t param )
{
    const char *obj;
    const char *object;
    char ifdev[IDENTIFY_SIZE];
    char path[PATHNAME_SIZE];

    /* get the com name */
    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    /* delete setup record file */
    project_var_path( path, sizeof(path), PROJECT_ID, "%s.st", obj );
    unlink( path );
    info( "%s shut", object );
    /* call the offline */
    com_srun( NETWORK_COM, "offline", object );
    /* stop the service */
    serv_stop( "%s-ipv4", object );
    serv_stop( "%s-ipv6", object );
    serv_stop( "%s", object );
    /* tell the ifdev down */
    if ( ifdev_get( object, ifdev, sizeof(ifdev) ) != NULL )
    {
        com_scall( ifdev, "down", NULL );
    }
	/* clear the reconnect count */
	count_set( object, "reconfailed", 0 );

    return ttrue;
}
talk_t _service( obj_t this, param_t param )
{
    int check;
    talk_t v;
    talk_t cfg;
    talk_t info;
	boole persist;
	boole castup;
    const char *ptr;
    const char *obj;
    const char *object;
	const char *mode;
	const char *method;
    char ifdev[IDENTIFY_SIZE];
    char device[IDENTIFY_SIZE];
    char buf[IDENTIFY_SIZE];

    /* get the com name */
    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    /* offline to clear first */
    serv_stop( "%s-ipv4", object );
    serv_stop( "%s-ipv6", object );
    if ( com_srun( NETWORK_COM, "extern", object ) == ttrue )
    {
        com_srun( NETWORK_COM, "offline", object );
    }
    /* get the configure */
    cfg = config_get( this, NULL ); 
    if ( cfg == NULL )
    {
        sleep( 10 );
        return tfalse;
    }
	/* get the mode and method */
	mode = json_get_string( cfg, "mode" );
	method = json_get_string( cfg, "method" );
	/* get the ifdev */
    if ( ifdev_get( object, ifdev, sizeof(ifdev) ) == NULL )
    {
        talk_free( cfg );
        sleep( 5 );
        return tfalse;
    }
	if ( com_sexist( ifdev, NULL) == false )
	{
        talk_free( cfg );
        sleep( 5 );
        return tfalse;
	}
    /* failed to restart */
    v = json_get_value( cfg, "failed" );
    if ( count_failed( object, ifdev, v ) != 0 )
    {
        talk_free( cfg );
        return tfalse;
    }
	/* reset modem may cause modem@lte to quit in ltecon */
	if ( serv_pid( ifdev ) < 0 )
	{
		com_scall( ifdev, "setup", NULL );
        sleep( 5 );
        return tfalse;
	}

	/* get advise mode */
	if ( mode == NULL || *mode == '\0' )
	{
		if ( com_sexist( ifdev, "advise" ) == true )
		{
			mode = com_scalls( ifdev, "advise", NULL, buf, sizeof(buf) );
		}
		if ( mode == NULL || *mode == '\0' )
		{
			mode = "dhcpc";
		}
	}
    /* setting the ifdev up */
    ndebug( "%s up", ifdev );
    if ( mode != NULL && 0 == strcasecmp( mode, "ppp" ) )
	{
		if ( com_scall( ifdev, "pppup", NULL ) != ttrue )
		{
			talk_free( cfg );
			sleep( 5 );
			return tfalse;
		}
	}
	else
	{
	    if ( com_scall( ifdev, "qmiup", NULL ) != ttrue )
	    {
	        talk_free( cfg );
	        sleep( 5 );
	        return tfalse;
	    }
	}
    /* connect */
    ndebug( "%s connect", ifdev );
    if ( com_scall( ifdev, "connect", NULL ) != ttrue )
    {
        talk_free( cfg );
        sleep( 5 );
        return tfalse;
    }
    /* get the ifdev device */
    ptr = com_scalls( ifdev, "device", NULL, device, sizeof(device) );
    if ( ptr == NULL )
    {
        fault( "%s device get error", ifdev );
        talk_free( cfg );
        sleep( 5 );
        return tfalse;
    }
    /* set the mac */
    ptr = json_get_string( cfg, "mac" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        com_srun( ifdev, "setmac", ptr );
    }

	/* SLAAC setting for IPv6 */
	if ( method != NULL && 0 == strcmp( method, "slaac" ) )
	{
		slaac_ip_enable( device );
	}
	else if ( method != NULL && 0 == strcmp( method, "disable" ) )
	{
		slaac_ip_disable( device );
	}

	/* static ip setting for IPv4 */
	if ( mode != NULL && 0 == strcmp( mode, "static" ) )
	{
		/* set the static ip */
		v = json_get_value( cfg, "static" );
		static_ip_enable( device, v );
	}

	/* static ip setting for IPv6 */
	if ( method != NULL && 0 == strcmp( method, "manual" ) )
	{
		/* set the static ip */
		v = json_get_value( cfg, "manual" );
		manual_ip_enable( device, v );
	}

    /* count failed */
    count_plus( object, "reconfailed" );
    /* check connected */
    check = 0;
    while( check < 60 )
    {
        if ( com_scall( ifdev, "connected", NULL ) == ttrue )
        {
            ndebug( "%s connected ready", ifdev );
            break;
        }
        check++;
        sleep( 1 );
    }
    if ( check >= 60 )
    {
        warn( "%s connect timeout", ifdev );
        com_scall( ifdev, "down", NULL );
        talk_free( cfg );
        return tfalse;
    }

	/* many process */
	info = json_create();
	persist = castup = false;

	/* static setting */
	if ( mode != NULL && 0 == strcmp( mode, "static" ) )
	{
		if ( mode_static( object, ifdev, device, cfg, info ) == true )
		{
			castup = true;
		}
	}
	if ( method != NULL && 0 == strcmp( method, "manual" ) )
	{
		if ( method_manual( object, ifdev, device, cfg, info ) == true )
		{
			castup = true;
		}
	}

	/* automatic setting */
	if ( mode != NULL && 0 == strcmp( mode, "dhcpc" ) )
	{
		persist = true;
		ndebug( "%s dhcp connect starting", ifdev );
		joint_casts( "network/lining", object );
		v = json_get_value( cfg, "static" );
		dhcp_client_connect( object, ifdev, device, v );
	}
	if ( method != NULL && 0 == strcmp( method, "automatic" ) )
	{
		persist = true;
		ndebug( "%s dhcpv6 connect starting", ifdev );
		joint_casts( "network/lining", object );
		v = json_get_value( cfg, "static" );
		dhcpv6_client_connect( object, ifdev, device, v );
	}

	/* ppp setting */
	if ( mode != NULL && 0 == strcmp( mode, "ppp" ) )
	{
		persist = true;
		ndebug( "%s ppp connect starting", ifdev );
		joint_casts( "network/lining", object );
		/* get the ifdev info */
		v = com_srun( ifdev, "status", NULL );
		if ( v == NULL )
		{
			talk_free( cfg );
			sleep( 5 );
			return tfalse;
		}
		ppp_client_connect( object, ifdev, cfg, v );
		talk_free( v );
	}

	if ( castup == true )
	{
		com_sgo( NETWORK_COM, "online", info );
	}
    talk_free( info );
	if ( persist == false )
	{
		serv_off( object );
	}
    talk_free( cfg );
    return tfalse;
}



talk_t _online( obj_t this, param_t param )
{
    talk_t v;
    talk_t cfg;
    talk_t info;
    const char *ptr;
    const char *obj;
    const char *object;
    const char *ifdev;
    const char *device;
    const char *dns;
    const char *dns2;
    char path[PATHNAME_SIZE];

    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    /* get the configure */
    cfg = config_get( this, NULL ); 
    if ( cfg == NULL )
    {
        return tfalse;
    }
    /* get info */
    info = param_talk( param, 1 );
    device = json_get_string( info, "device" );
    if ( device == NULL )
    {
        return tfalse;
    }
    ifdev = json_get_string( info, "ifdev" );
    /* dns */
    dns = json_get_string( info, "dns" );
    dns2 = json_get_string( info, "dns2" );
    /* custom dns */
	v = NULL;
    ptr = json_get_string( cfg, "mode" );
    if ( ptr != NULL )
    {
        v = json_get_value( cfg, ptr );
        ptr = json_get_string( v, "custom_dns" );
        if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
        {
            dns = json_get_string( v, "dns" );
            dns2 = json_get_string( v, "dns2" );
        }
    }
    ptr = json_get_string( info, "domain" );
    if ( (ptr != NULL && *ptr != '\0')
        || (dns != NULL && *dns != '\0') 
        || (dns2 != NULL && *dns2 != '\0') 
        )
    {
        snprintf( path, sizeof(path), "%s/%s", RESOLV_DIR, object );
        unlink( path );
        if ( ptr != NULL && *ptr != '\0' )
        {
            append_string( path, "search %s\n", ptr );
        }
        if ( dns != NULL && *dns != '\0'  )
        {
            append_string( path, "nameserver %s\n", dns );
            /* the ifname resolv may caanot work at other ifname */
            route_switch( NULL, dns, NULL, true, info );
        }
        if ( dns2 != NULL && *dns2 != '\0'  )
        {
            append_string( path, "nameserver %s\n", dns2 );
            /* the ifname resolv may caanot work at other ifname */
            route_switch( NULL, dns2, NULL, true, info );
        }
    }
    /* masq */
    ndebug( "shell( iptables -t nat -D %s -o %s -j MASQUERADE >/dev/null 2>&1 )", MASQ_CHAIN, device );
    shell( "iptables -t nat -D %s -o %s -j MASQUERADE >/dev/null 2>&1", MASQ_CHAIN, device );
    ptr = json_get_string( cfg, "masq" );
    if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
    {
        ndebug( "shell( iptables -t nat -A %s -o %s -j MASQUERADE )", MASQ_CHAIN, device );
        shell( "iptables -t nat -A %s -o %s -j MASQUERADE", MASQ_CHAIN, device );
    }
    /* ppp tcp mss */
    if ( 0 == strncmp( device, "ppp", 3 ) )
    {
        ptr = json_get_string( v, "txqueuelen" );
        if ( ptr == NULL || *ptr == '\0' )
        {
            ptr = "500";
        }
        ndebug( "shell( ifconfig %s txqueuelen %s)", device, ptr );
        ndebug( "shell( iptables -t mangle -D POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu )", device );
        ndebug( "shell( iptables -t mangle -A POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu )", device );
        shell( "ifconfig %s txqueuelen %s", device, ptr );
        shell( "iptables -t mangle -D POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu >/dev/null 2>&1", device );
        shell( "iptables -t mangle -A POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu", device );
    }
    /* tell the ifdev */
    if ( ifdev != NULL )
    {
        com_srun( ifdev, "online", object );
    }
    /* clear the failed count, dnon't clear at the icmp keeplive(clear in the keeplive) */
    v = json_get_value( cfg, "keeplive" );
    ptr = json_get_string( v, "type" );
    if ( ptr == NULL || 0 != strcmp( ptr, "icmp" ) )
    {
		count_set( object, "reconfailed", 0 );
    }

    talk_free( cfg );
    return ttrue;
}
talk_t _offline( obj_t this, param_t param )
{
    talk_t cfg;
    talk_t info;
    const char *ptr;
    const char *obj;
    const char *object;
    const char *device;
    const char *ifdev;
    char path[PATHNAME_SIZE];

    /* get the com name */
    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    /* get the configure */
    cfg = config_get( this, NULL ); 
    if ( cfg == NULL )
    {
        return tfalse;
    }
    /* get info */
    info = param_talk( param, 1 );
    device = json_get_string( info, "device" );
    if ( device == NULL )
    {
        return tfalse;
    }
    /* dns file */
    snprintf( path, sizeof(path), "%s/%s", RESOLV_DIR, object );
    unlink( path );
    /* masq */
    ptr = json_get_string( cfg, "masq" );
    if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
    {
        ndebug( "shell( iptables -t nat -D %s -o %s -j MASQUERADE >/dev/null 2>&1 )", MASQ_CHAIN, device );
        shell( "iptables -t nat -D %s -o %s -j MASQUERADE >/dev/null 2>&1", MASQ_CHAIN, device );
    }
    /* ppp tcp mss */
    if ( 0 == strncmp( device, "ppp", 3 ) )
    {
        ndebug( "shell( iptables -t mangle -D POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu >/dev/null 2>&1 )", device );
        shell( "iptables -t mangle -D POSTROUTING -o %s -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1400:1536 -j TCPMSS --clamp-mss-to-pmtu >/dev/null 2>&1", device );
    }
    /* tell the ifdev */
    ifdev = json_get_string( info, "ifdev" );
    if ( ifdev != NULL )
    {
        com_srun( ifdev, "offline", object );
    }

    talk_free( cfg );
    return ttrue;
}
talk_t _ifdev( obj_t this, param_t param )
{
    const char *obj;
    const char *object;
    char ifdev[IDENTIFY_SIZE];

    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return NULL;
    }
    object = obj_string( this );
    if ( ifdev_get( object, ifdev, sizeof(ifdev) ) != NULL )
    {
        return phrase_create( ifdev );
    }
    return NULL;
}
talk_t _device( obj_t this, param_t param )
{
    talk_t ret;
    talk_t info;
    const char *obj;
    const char *object;
    char ifdev[PATHNAME_SIZE];
    char path[PATHNAME_SIZE];

    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID) )
    {
        return NULL;
    }
    object = obj_string( this );
    project_var_path( path, sizeof(path), NETWORK_PROJECT, "%s.ol", object );
    info = json_load( path );
    ret = json_cut_value( info, "device" );
    if ( ret == NULL )
    {
        if ( ifdev_get( object, ifdev, sizeof(ifdev) ) != NULL )
        {
            ret = com_scall( ifdev, "device", NULL );
        }
    }
    talk_free( info );
    return ret;
}
talk_t _status( obj_t this, param_t param )
{
    talk_t ret;
    talk_t cfg;
    talk_t info;
	boole linkup;
    struct stat st;
    boole keeplive;
    const char *ptr;
    const char *obj;
    const char *object;
    const char *device;
    char ifdev[PATHNAME_SIZE];
    char buf[PATHNAME_SIZE];

    /* get the com name */
    obj = obj_name( this );
    if ( 0 == strcmp( obj, COM_ID ) )
    {
        return tfalse;
    }
    object = obj_string( this );
    ret = NULL;
	linkup = false;
    /* get the configure */
    cfg = config_get( this, NULL ); 
    ifdev_get( object, ifdev, sizeof(ifdev) );
    /* get the keeplive config */
    keeplive = false;
    info = json_get_value( cfg, "keeplive" );
    ptr = json_get_string( info, "type" );
    if ( ptr != NULL && 0 == strcmp( ptr, "icmp" ) )  //dns will failed because dhcps need it ok to full the dns host
    {
        keeplive = true;
    }
    /* get the online status */
    project_var_path( buf, sizeof(buf), NETWORK_PROJECT, "%s.ol", object );
    if ( stat( buf, &st ) != 0 )
    {
        ret = json_create();
		if ( com_sexist( ifdev, "device" ) == true )
		{
	        device = com_scalls( ifdev, "device", NULL, NULL, 0 );
	        if ( device != NULL )
	        {
	            json_set_string( ret, "device", device );
	            if ( netcard_info( device, NULL, 0, NULL, 0, NULL, 0, buf, sizeof(buf) ) == 0 )
	            {
	                json_set_string( ret, "mac", buf );
	            }
	        }
		}
        if ( serv_pid( object ) >= 0 )
        {
            json_set_string( ret, "status", "uping" );
        }
        else
        {
            json_set_string( ret, "status", "down" );
        }
    }
    else
    {
        char ip[16];
        char dstip[16];
        char mask[16];
        unsigned long long rt_bytes, rt_packets, rt_errs, rt_drops, tt_bytes, tt_packets, tt_errs, tt_drops;
        ip[0] = dstip[0] = mask[0] = '\0';
        rt_bytes = rt_packets = rt_errs = rt_drops = tt_bytes = tt_packets = tt_errs = tt_drops = 0;
        ret = json_load( buf );
        device = json_get_string( ret, "device" );
        netcard_info( device, ip, sizeof(ip), dstip, sizeof(dstip), mask, sizeof(mask), NULL, 0 );
		/* IPv4 */
        if ( netcard_isflags( device, IFF_UP ) == 0 || *ip == '\0' )
        {
            if ( serv_pid( object ) >= 0 )
            {
                json_set_string( ret, "status", "uping" );
            }
            else
            {
                json_set_string( ret, "status", "down" );
            }
        }
        else
        {
        	linkup = true;
            json_set_string( ret, "status", "up" );
            json_set_string( ret, "ip", ip );
            json_set_string( ret, "dstip", dstip );
            json_set_string( ret, "mask", mask );
            /* custom dns */
            ptr = json_get_string( cfg, "mode" );
            if ( ptr != NULL )
            {
                info = json_get_value( cfg, ptr );
                ptr = json_get_string( info, "custom_dns" );
                if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
                {
                    json_set_string( ret, "dns", json_get_string( info, "dns" ) );
                    json_set_string( ret, "dns2", json_get_string( info, "dns2" ) );
                }
            }
			/* ipv6 */
            ptr = json_get_string( cfg, "method" );
            if ( ptr != NULL && *ptr != '\0' && 0 != strcmp( ptr, "disable" ) )
            {
				int t;
            	int rc;
				char addrname[IDENTIFY_SIZE];
				char host[NI_MAXHOST];
				struct sockaddr_in6 *in6;
            	struct ifaddrs *ifaddr, *ifa;
				json_set_string( ret, "method", ptr );
				if ( getifaddrs( &ifaddr ) == 0 )
				{
					t = 1;
					for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next )
					{
						in6 = (struct sockaddr_in6 *)ifa->ifa_addr;
						if ( ifa->ifa_addr == NULL )
						{
							continue;
						}
						if ( ifa->ifa_addr->sa_family != AF_INET6 )
						{
							continue;
						}
						if ( 0 != strcmp( ifa->ifa_name, device ) )
						{
							continue;
						}
						rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST );
						if ( rc == 0 )
						{
							if ( t <= 1 )
							{
								strcpy( addrname, "addr" );
							}
							else
							{
								sprintf( addrname, "addr%d", t );
							}
							json_set_string( ret, addrname, host );
							t++;
							//printf("dev: %-8s address: <%s> scope %d\n", ifa->ifa_name, host, in6->sin6_scope_id);
						}
					}
					freeifaddrs(ifaddr);					
				}
                info = json_get_value( cfg, ptr );
                ptr = json_get_string( info, "custom_resolve" );
                if ( ptr != NULL && 0 == strcmp( ptr, "enable" ) )
                {
                    json_set_string( ret, "resolve", json_get_string( info, "resolve" ) );
                    json_set_string( ret, "resolve2", json_get_string( info, "resolve2" ) );
                }
            }			
            /* get the keeplive status */
            if ( keeplive == true )
            {
                project_var_path( buf, sizeof(buf), NETWORK_PROJECT, "%s.kp", object );
                ptr = read_string( buf, NULL, 0 );
                if ( ptr == NULL || *ptr == '\0' )
                {
                    json_set_string( ret, "keeplive", "failed" );
                }
                else
                {
                    json_set_string( ret, "keeplive", ptr );
                }
            }
        }
        buf[0] = '\0';
        netcard_info( device, NULL, 0, NULL, 0, NULL, 0, buf, sizeof(buf) );
        if ( *buf == '\0' || 0 == strcmp( buf, "00:00:00:00:00:00" ) || 0 == strcasecmp( buf, "ff:ff:ff:ff:ff:ff" ) ) // ppp interface mac
        {
            ptr = com_scalls( ifdev, "device", NULL, NULL, 0 );
            netcard_info( ptr, NULL, 0, NULL, 0, NULL, 0, buf, sizeof(buf) );
        }
        json_set_string( ret, "mac", buf );
        netcard_flew( device, &rt_bytes, &rt_packets, &rt_errs, &rt_drops, &tt_bytes, &tt_packets, &tt_errs, &tt_drops );
        snprintf( buf, sizeof(buf), "%llu", rt_bytes );
        json_set_string( ret, "rx_bytes", buf );
        snprintf( buf, sizeof(buf), "%llu", rt_packets );
        json_set_string( ret, "rx_packets", buf );
        snprintf( buf, sizeof(buf), "%llu", tt_bytes );
        json_set_string( ret, "tx_bytes", buf );
        snprintf( buf, sizeof(buf), "%llu", tt_packets );
        json_set_string( ret, "tx_packets", buf );
    }
    /* get the mode of configure */
    json_set_string( ret, "mode", json_get_string( cfg, "mode" ) );
    /* get the ifdev info */
	info = NULL;
	if ( (com_sexist( ifdev, "status" ) == true) && (NULL != ( info = com_srun( ifdev, "status", NULL ) )) )
	{
	    /* clear the device needless attr */
	    json_clear_axp ( info, "busid" );
	    json_clear_axp ( info, "deviceid" );
	    json_clear_axp ( info, "stty" );
	    json_clear_axp ( info, "mtty" );
	    json_clear_axp ( info, "gtty" );
	    json_clear_axp ( info, "site" );
	    json_clear_axp ( info, "devcom" );
	    json_clear_axp ( info, "drvcom" );
	    json_clear_axp ( info, "device" );
		if ( linkup )
		{
			json_clear_axp ( ret, "error" );
		}
	    json_modify( info, ret );
	    talk_free( info );
	}
	else
	{
		json_set_string( ret, "error", "nodevice" );
	}
    talk_free( cfg );
    /* ontime to livetime_string */
    ptr = json_get_string( ret, "ontime" );
    if ( ptr != NULL && *ptr != '\0' )
    {
        json_set_string( ret, "livetime", livetime_count( ptr, buf, sizeof(buf) ) );
    }

    return ret;
}



boole _set( obj_t this, path_t path, talk_t v )
{
    boole ret;
    boole dret;
    talk_t bak;
    talk_t cfg;
    talk_t mcfg;
    talk_t axp;
    talk_t info;
    const char *ptr;
    const char *object;
    char ifdev[IDENTIFY_SIZE];

    /* get the modem */
    object = obj_string( this );
    ifdev[0] = '\0';
    ifdev_get( object, ifdev, sizeof(ifdev) );

    ret = dret = false;
    ptr = path_get( path, 1 );
    if ( ptr == NULL || *ptr == '\0' )
    {
        /* delete the configure */
        if ( v == NULL )
        {
            dret = config_sset( ifdev, NULL, NULL );
            ret = config_set( this, NULL, NULL );
        }
        else
        {
            /* separation the modem cfg and ifname cfg */
            axp = NULL;
            cfg = json_create();
            mcfg = json_create();
            while ( NULL != ( axp = json_each( v, axp ) ) )
            {
                ptr = axp_get_attr( axp );
                info = talk_dup( axp_get_value( axp ) );
				if ( 0 == strcmp( ptr, "mode" ) )
				{
                    json_set_value( mcfg, ptr, talk_dup(info) );
                    json_set_value( cfg, ptr, info );
				}
                else if ( 0 == strcmp( ptr, "checksim" )
                    || 0 == strcmp( ptr, "needsim" )
                    || 0 == strcmp( ptr, "checksim2reset" )
                    || 0 == strcmp( ptr, "checknet" )
                    || 0 == strcmp( ptr, "neednet" )
                    || 0 == strcmp( ptr, "checknet2reset" )
                    || 0 == strcmp( ptr, "checksig" )
                    || 0 == strcmp( ptr, "needsig" )
                    || 0 == strcmp( ptr, "checksig2reset" )
                    || 0 == strcmp( ptr, "watch" )
                    || 0 == strcmp( ptr, "watchnet" )
                    || 0 == strcmp( ptr, "watchnet_mode" )
                    || 0 == strcmp( ptr, "watchnet_reset" )
                    || 0 == strcmp( ptr, "pin" )
                    || 0 == strcmp( ptr, "netmode" )
                    || 0 == strcmp( ptr, "band" )
                    || 0 == strcmp( ptr, "operator" )
                    || 0 == strcmp( ptr, "operator_cfg" )
                    || 0 == strcmp( ptr, "gnss" )
                    )
                {
                    json_set_value( mcfg, ptr, info );
                }
                else
                {
                    json_set_value( cfg, ptr, info );
                }
            }
            /* save the cfg */
			bak = config_sget( ifdev, NULL );
			if ( talk_equal( bak, mcfg ) == false )
			{
				dret = config_sset( ifdev, NULL, mcfg );
			}
			talk_free( bak );
			bak = config_get( this, NULL );
			if ( talk_equal( bak, cfg ) == false )
			{
	            ret = config_set( this, NULL, cfg );
			}
			else
			{
				ret = dret;
			}
			talk_free( bak );
            talk_free( cfg );
            talk_free( mcfg );
        }
    }
    else
    {
		if ( 0 == strcmp( ptr, "mode" ) )
		{
            dret = config_sset( ifdev, path_string(path), v );
            ret = config_set( this, path, v );
		}
		else if ( 0 == strcmp( ptr, "checksim" )
			|| 0 == strcmp( ptr, "needsim" )
			|| 0 == strcmp( ptr, "checksim2reset" )
			|| 0 == strcmp( ptr, "checknet" )
			|| 0 == strcmp( ptr, "neednet" )
			|| 0 == strcmp( ptr, "checknet2reset" )
			|| 0 == strcmp( ptr, "checksig" )
			|| 0 == strcmp( ptr, "needsig" )
			|| 0 == strcmp( ptr, "checksig2reset" )
			|| 0 == strcmp( ptr, "watch" )
			|| 0 == strcmp( ptr, "watchnet" )
			|| 0 == strcmp( ptr, "watchnet_mode" )
			|| 0 == strcmp( ptr, "watchnet_reset" )
			|| 0 == strcmp( ptr, "pin" )
			|| 0 == strcmp( ptr, "netmode" )
			|| 0 == strcmp( ptr, "band" )
			|| 0 == strcmp( ptr, "operator" )
			|| 0 == strcmp( ptr, "operator_cfg" )
			|| 0 == strcmp( ptr, "gnss" )
			)
        {
            dret = config_sset( ifdev, path_string(path), v );
			ret = dret;
        }
        else
        {
            ret = config_set( this, path, v );
        }
    }

    /* reset the lte */
    if ( dret == true )
    {
        com_scall( ifdev, "shut", NULL );
        com_scall( ifdev, "setup", NULL );
    }
    else if ( ret == true )
    {
        _shut( this, NULL );
        _setup( this, NULL );
    }
    return ret;
}
talk_t _get( obj_t this, path_t path )
{
    talk_t ret;
    talk_t cfg;
    talk_t mcfg;
    const char *object;
    char ifdev[IDENTIFY_SIZE];

    /* get the ifname cfg */
    cfg = config_get( this, NULL );

    /* get the modem cfg */
    mcfg = NULL;
    object = obj_string( this );
    if ( ifdev_get( object, ifdev, sizeof(ifdev) ) != NULL )
    {
        mcfg = config_sget( ifdev, NULL );
        /* combination the cfg */
        if ( cfg == NULL )
        {
            cfg = mcfg;
        }
        else
        {
            json_modify( mcfg, cfg );
            talk_free( mcfg );
        }
    }

    /* get the path attr */
    ret = talk_cut( cfg, path );
    if ( ret != cfg )
    {
        talk_free( cfg );
    }
    return ret;
}    



